<?php
/**
 * @author spenceryg
 * @date 2023/3/15
 * @time 6:10 下午
 * @focus love & life
 */


namespace app\common\lib;


use app\common\lib\verify\AliSmsVerify;
use app\common\lib\verify\VerifyFactory;
use app\common\model\ApikeyLibrary;
use app\common\model\GptToken;
use Firebase\JWT\JWT;
use GuzzleHttp\Client;
use Orhanerday\OpenAi\OpenAi;
use think\facade\Log;

class ChatGpt
{

    public static function chat($msg, $stream = false, $event_name = '', $returnLine = true, $event_handle = false,$is_json = true)
    {




        if ($stream) {

            $client = new Client([
                // Base URI is used with relative requests
                //'base_uri' => 'http://test.ai-dog.net/index/chat',
                'base_uri' =>  'https://open.bigmodel.cn/api/paas/v3/model-api/chatglm_std/sse-invoke',

                // You can set any number of default request options.
                'timeout' => 180,
            ]);

//        $token = GptToken::where('id' ,'=', 1)->value('token');
            $tokenModel = GptToken::where("category","智谱AI")->order('update_time', 'asc')->where('id', '>', 0)->find();
            $token = $tokenModel['token'];
            $tokenModel->force()->save();

            try{
                $res = $client->request('post', '', [
                    'verify' => false,
                    'headers' => [
                        'Authorization' => self::generate_token($token, 80),
                    ],
                    'stream' => $stream,
                    'json' => [
                        'prompt' => is_array($msg) ? $msg : [
                            [
                                "role" => "user",
                                "content" => $msg
                            ],
                        ],
                    ],
                ]);
            }catch (\Exception $e){
                $msg = $e->getResponse()->getReasonPhrase();
                return $msg;
            }

            $response_content = '';
            header('X-Accel-Buffering: no');
            header('Content-Type: text/event-stream');
            header('Cache-Control: no-cache');

            //跨域
            header('Access-Control-Allow-Origin: *');
            header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
            header('Access-Control-Allow-Headers: Content-Type');

            set_time_limit(0); //防止超时
            ob_end_clean();
            ob_implicit_flush(1);

            $body = $res->getBody();
            $arr = [];
            $last_data = '######';
            $titles = [];
            $one_line = '';
            $last_line = 0;

            while (!$body->eof()) {
                $arr[] = $body->read(200);
                $length = sizeof($arr);
                $words = explode("\n\n", $arr[$length - 1]);
                Log::channel('gpt')->info($words);
                foreach ($words as $word_index => $word) {
                    $full_data = [];
                    preg_match_all('/event:(.+)\nid:(.+)\ndata:(.*)/', $word, $full_data, PREG_SET_ORDER);
                    $finish_reason = null;
                    $this_str = '####';
                    if ($full_data) {
                        $finish_reason = $full_data[0][1];
                        $this_str = $full_data[0][3];
                    } else {
                        if ($word_index != 0) {
                            $last_data = $word;
                        } else {
                            preg_match_all('/event:(.+)\nid:(.+)\ndata:(.*)/', $last_data . $word, $full_data, PREG_SET_ORDER);
                            if($full_data){
                                $finish_reason = $full_data[0][1];
                                $this_str = $full_data[0][3];
                                $last_data = '######';
                            }

                        }
                    }


                    if ($returnLine ) {
                        if ($this_str !== '####') {
                            if ($this_str === '&&') {
                                if (trim($one_line) !== '') {
                                    $titles[] = trim($one_line);
                                    if ($event_handle) {
                                        self::$event_handle($event_name, $titles[sizeOf($titles) - 1]);
                                    } else {
                                        $c = "event:{$event_name}" . PHP_EOL; //定义事件
                                        $c .= "data:" . $titles[sizeOf($titles) - 1] . PHP_EOL; //推送内容
                                        echo $c . PHP_EOL;

                                    }
                                    $one_line = '';
                                }
                            } else {
                                $one_line .= $this_str;
                            }
                        }

                    } else {
                        if ($this_str !== '####' ) {
                            if ($event_handle) {
                                self::$event_handle($event_name, $titles[sizeOf($titles) - 1]);
                            } else {
                                $c = "event:{$event_name}" . PHP_EOL; //定义事件
                                if($is_json){
                                    $this_str_json = [
                                        'title' => $this_str,
                                    ];
//                                $c .= "data:" . $titles[sizeOf($titles) - 1] . PHP_EOL; //推送内容
                                    $c .= "data:" .json_encode($this_str_json). PHP_EOL; //推送内容
                                }else{
                                    $c .= "data:" . $this_str . PHP_EOL;
                                }

                                echo $c . PHP_EOL;
                                $response_content .= $this_str;
                            }
                        }
                    }

                }
            }
            $c = "event:{$event_name}" . PHP_EOL; //定义事件
            $c .= "data:end" . PHP_EOL; //推送内容
            echo $c . PHP_EOL;
            return $response_content;

        } else {
            $client = new Client([
                // Base URI is used with relative requests
                //'base_uri' => 'http://test.ai-dog.net/index/chat',
                'base_uri' =>  'https://open.bigmodel.cn/api/paas/v3/model-api/chatglm_std/sse-invoke',

                // You can set any number of default request options.
                'timeout' => 180,
            ]);

//        $token = GptToken::where('id' ,'=', 1)->value('token');
            $tokenModel = GptToken::order('update_time', 'asc')->where('id', '>', 0)->find();
            $token = $tokenModel['token'];
            $tokenModel->force()->save();

            try{
                $res = $client->request('post', '', [
                    'verify' => false,
                    'headers' => [
                        'Authorization' => self::generate_token($token, 80),
                    ],
                    'stream' => $stream,
                    'json' => [
                        'prompt' => is_array($msg) ? $msg : [
                            [
                                "role" => "user",
                                "content" => $msg
                            ],
                        ],
                    ],
                ]);
            }catch (\Exception $e){
                $msg = $e->getResponse()->getReasonPhrase();
                return $msg;
            }
            return $res;
        }

    }

    public static function getTitles($query): array
    {
        $res = self::chat($query);
        $arr = explode("\n", trim($res));
        $res = [];
        foreach ($arr as $item) {
            $res[] = explode(' ', $item, 2)[1];
        }
        return $res;
    }

    public static function sendEvent($event_name, $key)
    {
        Log::channel('title')->info($key);
        $res = '';
//        $arr = explode(' ', trim($key), 2);
//        if(sizeof($arr) == 2) {
//            $key = $arr[1];
//        }else{
//            $key = $arr[0];
//        }
        $key = preg_replace('/^\d{1,2}\./', '', $key);
        $data = [
            'title' => $key,
            'is_used' => 0
        ];
        $res = $res . "event:{$event_name}" . PHP_EOL;
        $res = $res . "data:" . json_encode($data) . PHP_EOL;
        echo $res . PHP_EOL;
    }


    public static function sendEventIsUsed($event_name, $key)
    {
        $res = '';
        $key = explode(' ', $key, 2)[1];
        $data = [
            'title' => $key,
            'is_used' => 0
        ];
        $res = $res . "event:{$event_name}" . PHP_EOL;
        $res = $res . "data:" . json_encode($data) . PHP_EOL;
        echo $res . PHP_EOL;
    }



    public static function generate_token($apikey, $exp_seconds): string
    {
        list($id, $secret) = explode('.', $apikey);

        if (!$id || !$secret) {
            throw new Exception('Invalid apikey');
        }

        $now_millis = round(microtime(true) * 1000);
        $payload = array(
            "api_key" => $id,
            "exp" => $now_millis + ($exp_seconds * 1000),
            "timestamp" => $now_millis
        );

        return JWT::encode($payload, $secret, 'HS256', null, array("alg" => "HS256", "sign_type" => "SIGN"));
    }




}
